#include <algorithm>
#include <string>
#include <iostream>

#include <config.h>
#include <dybuf.h>
#include <iohelper.h>
#include <tokenhelper.h>
#include <strhelper.h>

#include <foreach.h>

void Config::_clear()
{
    FOREACH(ValueMap::value_type& v, _values)
        delete v.second;

    _values.clear();
}

Config::Config()
    : _lr(LR_PREPARE)
    , _failed_at_lineno(1)
{
}

Config::Config(const char *fn)
    : _lr(LR_PREPARE)
    , _failed_at_lineno(1)
{
    load(fn);
}

bool Config::load(const char *fn)
{
    _clear();

    return append(fn);
}

bool Config::append(const char *fn)
{
    DyBuf *dybuf = io_read_file(fn);

    if (dybuf == NULL) {
        _lr = LR_IO_FAILED;
        return false;
    }

    TokenIn  in(dybuf);
    TokenOut key, val;

    for (;;) {
        if (tk_next(in, key) == false) {
            _lr = LR_SUCC;
            break;
        }

        if (tk_next(in, val) == false
            || key.lineno != val.lineno) {
            _lr = LR_INVALID_FORMAT;
            _failed_at_lineno = key.lineno;
            break;
        }

        value_t *v = new value_t;
        v->str = val.text;
        v->i   = 0;

        if (val.text.size() > 0 && CHAR_IS_DIG(val.text[0]))
            v->i = atoi(val.text.c_str());

        _values.insert(ValueMap::value_type(key.text, v));
    }

    delete dybuf;

    return _lr == LR_SUCC;
}

int Config::getInt(const std::string& key)
{
    ValueMap::iterator i = _values.find(key);
    return (i != _values.end()) ? i->second->i : 0;
}

std::string& Config::getStr(const std::string& key)
{
    static std::string nullStr;
    ValueMap::iterator i = _values.find(key);
    return (i != _values.end()) ? i->second->str : nullStr;
}

bool Config::exists(const std::string& key)
{
    return _values.find(key) != _values.end();
}

void Config::get_error_desc(std::string& res)
{
    const char *desc;

    switch (_lr) {
    case LR_IO_FAILED:
        desc = "io failed";
        break;

    case LR_INVALID_FORMAT:
        desc = "invalid format";
        break;

    case LR_SUCC:
        desc = "successful";
        break;

    case LR_PREPARE:
        desc = "prepare";
        break;

    default:
        desc = "unknown";
        break;
    }

    res = desc;

    res.push_back(':');
    res.push_back(' ');
    str_append(res, _failed_at_lineno);
}
